using System;
using System.Collections.Generic;
using Game.Scripts.Constant;
using Game.Scripts.Entity.Placeable;
using Game.Scripts.Interface;
using Game.Scripts.ScriptableObject;
using Game.Scripts.Service;
using Sirenix.OdinInspector;
using UnityEngine;
using Zenject;
using Random = System.Random;

namespace Game.Scripts.Entity
{
    [RequireComponent(typeof(BoxCollider))]
    public class RoadTileStandard : MonoBehaviour, IRoadTile
    {
        private RoadTileService _roadTileService;

        private ObjectPoolService _objectPoolService;

        private IkunBus _ikunBus;

        [SerializeField] private PlaceableConfigScriptableObject placeableConfigSO;

        [SerializeField] private Transform flexLayoutGroup;

        private readonly Random random = new Random();

        private Vector3 originalPosition;

        [SerializeField] private EndPoint endPoint;
        

        [Inject]
        public void Construct(IkunBus ikunBus, RoadTileService roadTileService, ObjectPoolService objectPoolService)
        {
            _ikunBus = ikunBus;
            _roadTileService = roadTileService;
            _objectPoolService = objectPoolService;
        }


        private void Awake()
        {
            originalPosition = transform.position;
        }

        private void Start()
        {
            SpawnPlaceableRoadItem();
        }


        private void OnTriggerEnter(Collider other)
        {
            if (other.gameObject.CompareTag(TagType.Player))
            {
                _roadTileService.tilePassedCount += 1;
                _roadTileService.HandleExtendRoad(this);
                _roadTileService.PlaceEndPointIfConfigured();
            }
        }

        public void MoveToFront(int distance)
        {
            // clear and then respawn item
            ClearPlaceableRoadItem();
            SpawnPlaceableRoadItem();
            transform.Translate(Vector3.forward * distance);
        }


        // https://stackoverflow.com/questions/69954685/how-to-shuffle-the-rows-of-a-2d-array-in-c-sharp
        private void ShuffleRow<T>(T[,] arr)
        {
            int height = arr.GetUpperBound(0) + 1;
            int width = arr.GetUpperBound(1) + 1;

            for (int i = 0; i < height; ++i)
            {
                int randomRow = random.Next(i, height);
                for (int j = 0; j < width; ++j)
                {
                    (arr[i, j], arr[randomRow, j]) = (arr[randomRow, j], arr[i, j]);
                }
            }
        }

        public void SpawnPlaceableRoadItem()
        {
            // add some randomness by shuffling row of config matrix
            ShuffleRow(placeableConfigSO.placeableMatrix);
            int width = placeableConfigSO.placeableMatrix.GetLength(0);
            int height = placeableConfigSO.placeableMatrix.GetLength(1);
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < height; j++)
                {
                    PlaceableType placeableType = placeableConfigSO.placeableMatrix[i, j];
                    int index = i + j * width;
                    GameObject obj = null;
                    if (placeableType == PlaceableType.Battery)
                    {
                        // get a random item except battery
                        if (_ikunBus.IfAtMaxSpeed())
                        {
                            obj = GetPlaceableItemByType(PlaceableType.Egg);
                        }
                        else
                        {
                            obj = GetPlaceableItemByType(placeableType);
                        }
                    }
                    else if (placeableType is PlaceableType.Egg or PlaceableType.Obstacle)
                    {
                        obj = GetPlaceableItemByType(placeableType);
                    }
                    else if (placeableType == PlaceableType.Heart)
                    {
                        if (_ikunBus.IfAtMaxHealth())
                        {
                            obj = GetPlaceableItemByType(PlaceableType.Egg);
                        }
                        else
                        {
                            obj = GetPlaceableItemByType(placeableType);
                        }
                    }

                    if (obj != null)
                    {
                        obj.transform.position = flexLayoutGroup.GetChild(index).position;
                        obj.transform.SetParent(flexLayoutGroup.GetChild(index));
                    }
                }
            }
        }


        private GameObject GetPlaceableItemByType(PlaceableType placeableType)
        {
            GameObject obj = placeableType switch
            {
                PlaceableType.Battery => _objectPoolService.batteryObjectPool.Get(),
                PlaceableType.Egg => _objectPoolService.eggObjectPool.Get(),
                PlaceableType.Obstacle => _objectPoolService.obstacleObjectPool.Get(),
                PlaceableType.Heart => _objectPoolService.heartObjectPool.Get(),
                _ => null
            };

            return obj;
        }


        public void ClearPlaceableRoadItem()
        {
            TraverseHierarchy(flexLayoutGroup, (obj) =>
            {
                // return remain items to the pool
                if (obj.activeInHierarchy)
                {
                    if (obj.GetComponent<Egg>() != null)
                    {
                        _objectPoolService.eggObjectPool.Release(obj);
                    }
                    else if (obj.GetComponent<Battery>() != null)
                    {
                        _objectPoolService.batteryObjectPool.Release(obj);
                    }
                    else if (obj.GetComponent<Heart>() != null)
                    {
                        _objectPoolService.heartObjectPool.Release(obj);
                    }
                    else if (obj.GetComponent<Obstacle>() != null)
                    {
                        _objectPoolService.obstacleObjectPool.Release(obj);
                    }
                }
            });
        }

        public void ResetPosition()
        {
            transform.position = originalPosition;
        }

        public void MarkEndPoint()
        {
            endPoint.gameObject.SetActive(true);
        }

        public void UnMarkEndPoint()
        {
            endPoint.gameObject.SetActive(false);
        }


        private void TraverseHierarchy(Transform root, Action<GameObject> nodeOperation = null)
        {
            foreach (Transform child in root)
            {
                nodeOperation?.Invoke(child.gameObject);
                TraverseHierarchy(child, nodeOperation);
            }
        }
    }
}